package me.chan.nio.channel;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;

@Slf4j
public class WritableServer {

    public static void main(String[] args) {
        try {
            ServerSocketChannel ssc = ServerSocketChannel.open();
            ssc.configureBlocking(false);   // non-blocking mode
            ssc.bind(new InetSocketAddress(8080));
            Selector selector = Selector.open();
            ssc.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                selector.select();      // block here
                Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
                while (iter.hasNext()) {
                    SelectionKey key = iter.next();
                    iter.remove();
                    if (key.isAcceptable()) {
                        SocketChannel socket = ssc.accept();
                        socket.configureBlocking(false);
                        // get the selection key related to this socket channel
                        SelectionKey sckey = socket.register(selector, SelectionKey.OP_READ);

                        // prepare the data for output
                        StringBuilder content = new StringBuilder();
                        for (int i=0; i < 5_000_000; i++) {
                            content.append("6");
                        }

                        ByteBuffer buffer = StandardCharsets.UTF_8.encode(content.toString());
                        int written = socket.write(buffer);
                        log.info("{} bytes has been written out by channel", written);

                        // could not complete writing by once
                        if (buffer.hasRemaining()) {
                            // interest OP_WRITE additional
                            sckey.interestOps(sckey.interestOps() + SelectionKey.OP_WRITE);

                            sckey.attach(buffer);
                        }
                    } else if (key.isWritable()) {
                        ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
                        SocketChannel sc = (SocketChannel) key.channel();
                        int written = sc.write(byteBuffer);
                        log.info("{} bytes has been written", written);
                        if (!byteBuffer.hasRemaining()) {       // implies the write work has done
                            key.attach(null);
                            key.interestOps(key.interestOps() - SelectionKey.OP_WRITE);
                        }
                    }
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}
